#pragma once

#include <iostream>
#include <queue>
#include "Cond.hpp"
#include "Mutex.hpp"

using namespace CondModule;
using namespace MutexModule;

const int default_cap = 5;

template <typename T>
class BlockingQueue
{
    bool isEmpty()
    {
        return _bq.empty();
    }
    bool isFull()
    {
        return _bq.size() >= _cap;
    }
public:
    BlockingQueue(int cap = default_cap)
        : _cap(cap)
        ,_p_sleep_num(0)
        ,_c_sleep_num(0)
    {
    }
    void Push(const T& in)
    {
        // 生产者
        LockGuard lockguard(_mutex);
        while(isFull())
        {
            ++_p_sleep_num;
            std::cout << "生产者因为满而阻塞: " << _p_sleep_num << std::endl;
            _full_cond.Wait(_mutex); // 生产者因为满而阻塞
            --_p_sleep_num;
        }
        _bq.push(in);
        if(_c_sleep_num > 0)
        {
            _empty_cond.Signal();// 唤醒一个因为空而阻塞的消费者
            std::cout << "唤醒一个因为空而阻塞的消费者" << std::endl;
        }
    }
    T& Pop()
    {
        // 消费者
        LockGuard lockguard(_mutex);
        while(isEmpty())
        {
            ++_c_sleep_num;
            std::cout << "消费者者因为空而阻塞: " << _c_sleep_num << std::endl;
            _empty_cond.Wait(_mutex); // 消费者者因为空而阻塞
            --_c_sleep_num;
        }
        T& data = _bq.front();
        _bq.pop();

        if(_p_sleep_num > 0)
        {
            _full_cond.Signal(); // 唤醒一个因为满而阻塞的生产者
            std::cout << "唤醒一个因为满而阻塞的生产者" << std::endl;
        }
        return data;
    }

private:
    std::queue<T> _bq;
    int _cap;

    Mutex _mutex;

    Cond _empty_cond;
    Cond _full_cond;

    int _p_sleep_num;
    int _c_sleep_num;
};